package com.kamistoat.meimeistore.commoncore.utils;

import com.kamistoat.meimeistore.commoncore.constant.SecurityConstants;
import com.kamistoat.meimeistore.commoncore.constant.TokenConstants;
import com.kamistoat.meimeistore.commoncore.text.Convert;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Map;

/**
 * Jwt工具类。
 * Jwt是SpringSecurity中的概念，是一种JSON风格的轻量级授权和身份认证规范。可实现无状态分布式的Web应用授权。
 * Jwt相比与完整的用户令牌，更加轻量化。一般来说我们都是使用用户令牌的几个唯一字段来构建Jwt。
 * <p>
 * 在本项目中，用户令牌(LoginUser类)包含非常多的内容，且很多内容只有后端需要，前端并不需要，因此完整的用户令牌会缓存在后端。
 * 然后从用户令牌中提取出 token、userId、userName，仅使用这三个字段来构建Jwt，然后将Jwt传递给前端。
 * 前端每次发送请求时会在请求头中携带Jwt (在请求头中以 Authorization 为 key， 以 Jwt数据为 value)。
 * 显然，Jwt数据也是通过token唯一标识的，且和对应的完整用户令牌的token相同，因此后端可以根据前端中jwt数据的token来寻找对应的完整用户令牌进行校验。
 * <p>
 * 需要引入 jjwt 依赖
 */
public class JwtUtils {
    public static String secret = TokenConstants.SECRET;

    /**
     * 根据k-v数据生成 Jwt。
     * 该方法和输入的k-v中有那些字段毫无关系。只是本项目中固定使用 用户令牌的 token、userId、userName 来构建Jwt，
     * 因此输入 claims中应该只含有 token/userId/userName 三个字段的k-v对
     *
     * @param claims Map<>实现类，将根据内容制作Jwt。本项目中应只含有 token/userId/userName 三个字段的k-v对
     * @return 构建好的 jwt String
     */
    public static String createToken(Map<String, Object> claims) {
        String jwtString = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
        return jwtString;
    }

    /**
     * 从jwt中解构出k-v格式的数据，制作成Map。
     *
     * @param jwtString Jwt数据
     * @return 解构出的k-v数据对。Claims其实就是一个Map类
     */
    public static Claims parseToken(String jwtString) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(jwtString).getBody();
    }

    /**
     * 从jwt中解构出k-v格式的数据，制作成Map。
     * 然后从Map中提取出 userKey(其实就是 jwt和用户令牌的唯一标识token)
     *
     * @param jwtString jwt数据
     * @return userKey(token)
     */
    public static String getUserKey(String jwtString) {
        Claims claims = parseToken(jwtString);
        return getValue(claims, SecurityConstants.USER_KEY);
    }

    /**
     * 直接在 k-v格式的Map中获取userKey
     *
     * @param claims k-v格式的 Map数据
     * @return 用户ID
     */
    public static String getUserKey(Claims claims) {
        return getValue(claims, SecurityConstants.USER_KEY);
    }

    /**
     * 从jwt中解构出k-v格式的数据，制作成Map。
     * 然后从Map中提取出 userId
     *
     * @param jwtString jwt数据
     * @return userId
     */
    public static String getUserId(String jwtString) {
        Claims claims = parseToken(jwtString);
        return getValue(claims, SecurityConstants.DETAILS_USER_ID);
    }

    /**
     * 直接在 k-v格式的Map中获取userId
     *
     * @param claims k-v格式的 Map数据
     * @return userId
     */
    public static String getUserId(Claims claims) {
        return getValue(claims, SecurityConstants.DETAILS_USER_ID);
    }

    /**
     * 从jwt中解构出k-v格式的数据，制作成Map。
     * 然后从Map中提取出 userName
     *
     * @param jwtString jwt数据
     * @return userName
     */
    public static String getUserName(String jwtString) {
        Claims claims = parseToken(jwtString);
        return getValue(claims, SecurityConstants.DETAILS_USERNAME);
    }

    /**
     * 直接在 k-v格式的Map中获取userName
     *
     * @param claims k-v格式的 Map数据
     * @return userName
     */
    public static String getUserName(Claims claims) {
        return getValue(claims, SecurityConstants.DETAILS_USERNAME);
    }

    /**
     * 从k-v形式的 Claims中获取对应key的value
     *
     * @param claims 实际上就是一个 Map<> 的实现类
     * @param key    键
     * @return 值
     */
    public static String getValue(Claims claims, String key) {
        return Convert.toStr(claims.get(key), "");
    }
}
